/* {{{ License.
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *///}}}
// :indentSize=4:lineSeparator=\n:noTabs=false:tabSize=4:folding=explicit:collapseFolds=0:
package org.mathpiper.lisp;

import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.mathpiper.builtin.BigNumber;
import org.mathpiper.builtin.BuiltinFunction;
import org.mathpiper.exceptions.EvaluationException;
import org.mathpiper.io.*;
import org.mathpiper.lisp.behaviours.BackQuoteSubstitute;
import org.mathpiper.lisp.behaviours.Substitute;
import org.mathpiper.lisp.collections.OperatorMap;
import org.mathpiper.lisp.cons.*;
import org.mathpiper.lisp.parametermatchers.Pattern;
import org.mathpiper.lisp.parametermatchers.PatternParameter;
import org.mathpiper.lisp.parsers.MathPiperParser;
import org.mathpiper.lisp.printers.MathPiperPrinter;
import org.mathpiper.lisp.tokenizers.MathPiperTokenizer;
import org.mathpiper.lisp.userfunctions.*;

public class Utility {

  public static final int ATOM = 1;
  public static final int NUMBER = 2;
  public static final int SUBLIST = 3;
  public static final int OBJECT = 4;
  static int log2_table_size = 32; // A lookup table of Ln(n)/Ln(2) for n = 1 ..
  // 32.
  // With this we don't have to use math.h if all we need is to convert the
  // number of digits from one base to another. This is also faster.
  // Generated by: PrintList(N(Ln(1 .. 32)/Ln(2)), ",") at getPrecision 40.
  private static double log2_table[] = {0., 1.,
      1.5849625007211561814537389439478165087598, 2.,
      2.3219280948873623478703194294893901758648,
      2.5849625007211561814537389439478165087598,
      2.807354922057604107441969317231830808641, 3.,
      3.1699250014423123629074778878956330175196,
      3.3219280948873623478703194294893901758648,
      3.4594316186372972561993630467257929587032,
      3.5849625007211561814537389439478165087598,
      3.7004397181410921603968126542566947336284,
      3.807354922057604107441969317231830808641,
      3.9068905956085185293240583734372066846246, 4.,
      4.0874628412503394082540660108104043540112,
      4.1699250014423123629074778878956330175196,
      4.2479275134435854937935194229068344226935,
      4.3219280948873623478703194294893901758648,
      4.3923174227787602888957082611796473174008,
      4.4594316186372972561993630467257929587032,
      4.5235619560570128722941482441626688444988,
      4.5849625007211561814537389439478165087598,
      4.6438561897747246957406388589787803517296,
      4.7004397181410921603968126542566947336284,
      4.7548875021634685443612168318434495262794,
      4.807354922057604107441969317231830808641,
      4.8579809951275721207197733246279847624768,
      4.9068905956085185293240583734372066846246,
      4.9541963103868752088061235991755544235489, 5.};
  public static java.util.zip.ZipFile zipFile = null;

  public static void applyPure(ConsPointer oper, ConsPointer args2,
      ConsPointer aResult, Environment aEnvironment) throws Exception {
    LispError.check(oper.car() instanceof ConsPointer,
        LispError.INVALID_ARGUMENT);
    LispError.check(((ConsPointer) oper.car()).getCons() != null,
        LispError.INVALID_ARGUMENT);
    ConsPointer oper2 = new ConsPointer();
    oper2.setCons(((ConsPointer) oper.car()).cdr().getCons());
    LispError.check(oper2.getCons() != null, LispError.INVALID_ARGUMENT);

    ConsPointer body = new ConsPointer();
    body.setCons(oper2.cdr().getCons());
    LispError.check(body.getCons() != null, LispError.INVALID_ARGUMENT);

    LispError.check(oper2.car() instanceof ConsPointer,
        LispError.INVALID_ARGUMENT);
    LispError.check(((ConsPointer) oper2.car()).getCons() != null,
        LispError.INVALID_ARGUMENT);
    oper2.setCons(((ConsPointer) oper2.car()).cdr().getCons());

    aEnvironment.pushLocalFrame(false, "Pure");
    try {
      while (oper2.getCons() != null) {
        LispError.check(args2.getCons() != null, LispError.INVALID_ARGUMENT);

        String var = (String) oper2.car();
        LispError.check(var != null, LispError.INVALID_ARGUMENT);
        ConsPointer newly = new ConsPointer();
        newly.setCons(args2.getCons().copy(false));
        aEnvironment.newLocalVariable(var, newly.getCons());
        oper2.setCons(oper2.cdr().getCons());
        args2.setCons(args2.cdr().getCons());
      }
      LispError.check(args2.getCons() == null, LispError.INVALID_ARGUMENT);
      aEnvironment.iLispExpressionEvaluator.evaluate(aEnvironment, aResult,
          body);
    } catch (EvaluationException e) {
      throw e;
    } finally {
      aEnvironment.popLocalFrame();
    }

  }

  public static void applyString(Environment aEnvironment, ConsPointer aResult,
      String aOperator, ConsPointer aArgs) throws Exception {
    LispError.check(isString(aOperator), LispError.NOT_A_STRING);

    Cons head = AtomCons.getInstance(aEnvironment, getSymbolName(aEnvironment,
        aOperator));
    head.cdr().setCons(aArgs.getCons());
    ConsPointer body = new ConsPointer();
    body.setCons(SublistCons.getInstance(head));
    aEnvironment.iLispExpressionEvaluator.evaluate(aEnvironment, aResult, body);
  }

  /**
   * Convert the number of bits in a given base to the number of digits. To make
   * sure there is no hysteresis, the returned value is rounded down.
   * 
   * @param bits
   * @param base
   * @return the number of digits
   * @throws java.lang.Exception
   */
  public static long bitsToDigits(long bits, int base) throws Exception {
    return (long) Math.floor(bits / log2TableLookup(base));
  }

  public static void defMacroRuleBase(Environment aEnvironment, int aStackTop,
      boolean aListed) throws Exception {
    // Get operator
    ConsPointer args = new ConsPointer();
    new ConsPointer();
    String orig = null;

    LispError.checkArgument(aEnvironment, aStackTop, BuiltinFunction
        .getArgumentPointer(aEnvironment, aStackTop, 1).getCons() != null, 1);
    orig = (String) BuiltinFunction.getArgumentPointer(aEnvironment, aStackTop,
        1).car();
    LispError.checkArgument(aEnvironment, aStackTop, orig != null, 1);

    // The arguments
    args.setCons(BuiltinFunction.getArgumentPointer(aEnvironment, aStackTop, 2)
        .getCons());
    LispError.checkIsList(aEnvironment, aStackTop, args, 2);

    // Finally define the rule base
    aEnvironment.declareMacroRulebase(
        Utility.getSymbolName(aEnvironment, orig), ((ConsPointer) args.car())
            .cdr(), aListed);

    // Return true
    Utility.putTrueInPointer(aEnvironment, BuiltinFunction
        .getTopOfStackPointer(aEnvironment, aStackTop));
  }

  public static void delete(Environment aEnvironment, int aStackTop,
      boolean aDestructive) throws Exception {
    ConsPointer evaluated = new ConsPointer();
    evaluated.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 1).getCons());
    LispError.checkIsList(aEnvironment, aStackTop, evaluated, 1);

    ConsPointer copied = new ConsPointer();
    if (aDestructive)
      copied.setCons(((ConsPointer) evaluated.car()).getCons());
    else
      Utility.flatCopy(copied, (ConsPointer) evaluated.car());

    ConsPointer index = new ConsPointer();
    index.setCons(BuiltinFunction
        .getArgumentPointer(aEnvironment, aStackTop, 2).getCons());
    LispError
        .checkArgument(aEnvironment, aStackTop, index.getCons() != null, 2);
    LispError.checkArgument(aEnvironment, aStackTop,
        index.car() instanceof String, 2);
    int ind = Integer.parseInt((String) index.car(), 10);
    LispError.checkArgument(aEnvironment, aStackTop, ind > 0, 2);

    ConsTraverser consTraverser = new ConsTraverser(copied);
    while (ind > 0) {
      consTraverser.goNext();
      ind--;
    }
    LispError.check(aEnvironment, aStackTop, consTraverser.getCons() != null,
        LispError.NOT_LONG_ENOUGH);
    ConsPointer next = new ConsPointer();
    next.setCons(consTraverser.cdr().getCons());
    consTraverser.getPointer().setCons(next.getCons());
    BuiltinFunction.getTopOfStackPointer(aEnvironment, aStackTop).setCons(
        SublistCons.getInstance(copied.getCons()));
  }

  /**
   * Convert the number of digits in given base to the number of bits. To make
   * sure there is no hysteresis, the returned value is rounded up.
   * 
   * @param digits
   * @param base
   * @return the number of bits
   * @throws java.lang.Exception
   */
  public static long digitsToBits(long digits, int base) throws Exception {
    return (long) Math.ceil(digits * log2TableLookup(base));
  }

  private static void doInternalLoad(Environment aEnvironment,
      MathPiperInputStream aInput) throws Exception {
    MathPiperInputStream previous = aEnvironment.iCurrentInput;
    try {
      aEnvironment.iCurrentInput = aInput;
      // TODO make "EndOfFile" a global thing
      // read-parse-evaluate to the end of file
      String eof = (String) aEnvironment.getTokenHash().lookUp("EndOfFile");
      boolean endoffile = false;
      MathPiperParser parser = new MathPiperParser(new MathPiperTokenizer(),
          aEnvironment.iCurrentInput, aEnvironment,
          aEnvironment.iPrefixOperators, aEnvironment.iInfixOperators,
          aEnvironment.iPostfixOperators, aEnvironment.iBodiedOperators);
      ConsPointer readIn = new ConsPointer();
      while (!endoffile) {
        // Read expression
        parser.parse(readIn);

        LispError.check(readIn.getCons() != null, LispError.READING_FILE);
        // check for end of file
        if (readIn.car() instanceof String && (String) readIn.car() == eof)
          endoffile = true;
        else {
          ConsPointer result = new ConsPointer();
          aEnvironment.iLispExpressionEvaluator.evaluate(aEnvironment, result,
              readIn);
          aEnvironment.setGlobalVariable("LoadResult", result, false);// Note:tk:added
          // to make
          // the
          // result
          // of
          // executing
          // Loaded
          // code
          // available.
        }
      }// end while.

    } catch (Exception e) {
      EvaluationException ee = new EvaluationException(e.getMessage(),
          aEnvironment.iCurrentInput.iStatus.lineNumber());
      throw ee;
    } finally {
      aEnvironment.iCurrentInput = previous;
    }
  }

  private static void doLoadDefFile(Environment aEnvironment,
      MathPiperInputStream aInput, DefFile def) throws Exception {
    MathPiperInputStream previous = aEnvironment.iCurrentInput;
    try {
      aEnvironment.iCurrentInput = aInput;
      String eof = (String) aEnvironment.getTokenHash().lookUp("EndOfFile");
      String end = (String) aEnvironment.getTokenHash().lookUp("}");
      boolean endoffile = false;

      MathPiperTokenizer tok = new MathPiperTokenizer();

      while (!endoffile) {
        // Read expression
        String token = tok.nextToken(aEnvironment.iCurrentInput, aEnvironment
            .getTokenHash());

        // check for end of file
        if (token == eof || token == end)
          endoffile = true;
        else {
          String str = token;
          MultipleArityUserFunction multiUser = aEnvironment
              .getMultipleArityUserFunction(str);
          if (multiUser.iFileToOpen != null)
            throw new EvaluationException("[" + str + "]"
                + "] : def file already chosen: "
                + multiUser.iFileToOpen.iFileName, -1);
          multiUser.iFileToOpen = def;
          multiUser.iFileLocation = def.fileName();
        }
      }
    } catch (Exception e) {
      throw e;
    } finally {
      aEnvironment.iCurrentInput = previous;
    }
  }

  public static String dumpRule(Branch branch, Environment aEnvironment,
      SingleArityBranchingUserFunction userFunction) {
    StringBuilder dumpResult = new StringBuilder();
    try {
      int precedence = branch.getPrecedence();
      ConsPointer predicatePointer1 = branch.getPredicatePointer();
      String predicate = "";
      String predicatePointerString = predicatePointer1.toString();
      if (predicatePointerString == null
          || predicatePointerString.equalsIgnoreCase("Empty."))
        predicate = "None.";
      else
        predicate = Utility.printExpression(predicatePointer1, aEnvironment, 0);

      if (predicate.equalsIgnoreCase("\"Pattern\"")) {
        predicate = "(Pattern) ";
        PatternBranch branchPattern = (PatternBranch) branch;
        Pattern pattern = branchPattern.getPattern();

        Iterator<String> variablesIterator = pattern.getVariables().iterator();
        String patternVariables = "";
        while (variablesIterator.hasNext()) {
          String patternVariable = variablesIterator.next();
          patternVariables += patternVariable + ", ";
        }
        if (patternVariables.contains(","))
          patternVariables = patternVariables.substring(0, patternVariables
              .lastIndexOf(","));

        Iterator<PatternParameter> parameterMatchersIterator = pattern
            .getParameterMatchers().iterator();
        String parameterTypes = "";
        while (parameterMatchersIterator.hasNext()) {
          PatternParameter parameter = parameterMatchersIterator.next();
          String parameterType = parameter.getType();
          parameterTypes += parameterType + ", ";
        }
        if (parameterTypes.contains(","))
          parameterTypes = parameterTypes.substring(0, parameterTypes
              .lastIndexOf(","));

        Iterator<ConsPointer> patternPredicatesIterator = pattern
            .getPredicates().iterator();
        while (patternPredicatesIterator.hasNext()) {
          ConsPointer predicatePointer = patternPredicatesIterator.next();
          String patternPredicate = Utility.printExpression(predicatePointer,
              aEnvironment, 0);
          predicate += patternPredicate + ", ";
        }
        /*
         * if (predicate.contains(",")) { predicate = predicate.substring(0,
         * predicate.lastIndexOf(",")); }
         */
        predicate += "\n    Variables: " + patternVariables + ", ";
        predicate += "\n    Types: " + parameterTypes;

      }// end if.

      Iterator<FunctionParameter> paremetersIterator = userFunction
          .getParameters();
      String parameters = "";
      boolean isHold = false;
      while (paremetersIterator.hasNext()) {
        FunctionParameter branchParameter = paremetersIterator.next();
        String parameter = branchParameter.getParameter();
        isHold = branchParameter.isHold();
        parameters += parameter + "<hold=" + isHold + ">, ";
      }
      if (parameters.contains(","))
        parameters = parameters.substring(0, parameters.lastIndexOf(","));

      String body = Utility.printExpression(branch.getBodyPointer(),
          aEnvironment, 0);
      body = body.replace(",", ", ");
      // System.out.println(data);

      String substitutedMacroBody = "";

      if (userFunction instanceof MacroUserFunction) {
        BackQuoteSubstitute backQuoteSubstitute = new BackQuoteSubstitute(
            aEnvironment);
        ConsPointer substitutedBodyPointer = new ConsPointer();
        Utility.substitute(substitutedBodyPointer, branch.getBodyPointer(),
            backQuoteSubstitute);
        substitutedMacroBody = Utility.printExpression(substitutedBodyPointer,
            aEnvironment, 0);
      }

      dumpResult.append("Precedence: " + precedence + ", ");
      dumpResult.append("\n" + "Parameters: " + parameters + ", ");
      dumpResult.append("\n" + "Predicates: " + predicate + ",    ");

      if (userFunction instanceof MacroUserFunction) {
        dumpResult.append("\n" + "Body: \n" + body + ", ");
        dumpResult.append("\n" + "Substituted Macro Body: \n"
            + substitutedMacroBody + "\n");
      } else
        dumpResult.append("\n" + "Body: \n" + body + "\n");

    } catch (Exception ex) {
      ex.printStackTrace();
    }
    return dumpResult.toString();

  }// end method.

  public static boolean equals(Environment aEnvironment,
      ConsPointer aExpression1, ConsPointer aExpression2) throws Exception {
    // Handle pointers to same, or null
    if (aExpression1.getCons() == aExpression2.getCons())
      return true;
    // LispError.check(aExpression1.type().equals("Number"),
    // LispError.INVALID_ARGUMENT);
    // LispError.check(aExpression2.type().equals("Number"),
    // LispError.INVALID_ARGUMENT);
    BigNumber n1 = (BigNumber) aExpression1.getCons().getNumber(
        aEnvironment.getPrecision());
    BigNumber n2 = (BigNumber) aExpression2.getCons().getNumber(
        aEnvironment.getPrecision());
    if (!(n1 == null && n2 == null)) {
      if (n1 == n2)
        return true;
      if (n1 == null)
        return false;
      if (n2 == null)
        return false;
      if (n1.equals(n2))
        return true;
      return false;
    }

    // Pointers to strings should be the same
    if (aExpression1.car() instanceof String
        && aExpression2.car() instanceof String)
      if (aExpression1.car() != aExpression2.car())
        return false;

    // Handle same sublists, or null
    if (aExpression1.car() == aExpression2.car())
      return true;

    // Now check the sublists
    if (aExpression1.car() instanceof ConsPointer) {
      if (!(aExpression2.car() instanceof ConsPointer))
        return false;
      ConsTraverser consTraverser1 = new ConsTraverser(
          (ConsPointer) aExpression1.car());
      ConsTraverser consTraverser2 = new ConsTraverser(
          (ConsPointer) aExpression2.car());

      while (consTraverser1.getCons() != null
          && consTraverser2.getCons() != null) {
        // compare two list elements
        if (!equals(aEnvironment, consTraverser1.getPointer(), consTraverser2
            .getPointer()))
          return false;

        // Step to rest
        consTraverser1.goNext();
        consTraverser2.goNext();
      }
      // Lists don't have the same length
      if (consTraverser1.getCons() != consTraverser2.getCons())
        return false; // Same!
      return true;
    }

    // expressions sublists are not the same!
    return false;
  }

  public static String findFile(String aFileName,
      InputDirectories aInputDirectories) throws Exception {
    InputStatus inputStatus = new InputStatus();
    String othername = aFileName;
    int i = 0;
    MathPiperInputStream f = openInputFile(othername, inputStatus);
    if (f != null)
      return othername;
    while (i < aInputDirectories.size()) {
      othername = (String) aInputDirectories.get(i) + aFileName;
      f = openInputFile(othername, inputStatus);
      if (f != null)
        return othername;
      i++;
    }
    return "";
  }

  public static void flatCopy(ConsPointer aResult, ConsPointer aOriginal)
      throws Exception {
    ConsTraverser orig = new ConsTraverser(aOriginal);
    ConsTraverser res = new ConsTraverser(aResult);

    while (orig.getCons() != null) {
      res.getPointer().setCons(orig.getCons().copy(false));
      orig.goNext();
      res.goNext();
    }
  }

  /**
   * Construct a {@link BigNumber}.
   * 
   * @param aEnvironment
   *          the current {@link Environment}.
   * @param aStackTop
   *          points to the the top of the argument stack.
   * @param aArgNr
   *          the index of the argument to be converted.
   * @return a BigNumber.
   * @throws java.lang.Exception
   */
  public static BigNumber getNumber(Environment aEnvironment, int aStackTop,
      int aArgNr) throws Exception {
    // LispError.check(BuiltinFunction.getArgumentPointer(aEnvironment,
    // aStackTop, aArgNr).type().equals("Number"), LispError.INVALID_ARGUMENT);
    BigNumber x = (BigNumber) BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, aArgNr).getCons().getNumber(aEnvironment.getPrecision());
    LispError.checkArgument(aEnvironment, aStackTop, x != null, aArgNr);
    return x;
  }

  public static String getSymbolName(Environment aEnvironment, String aSymbol) {
    if (aSymbol.charAt(0) == '\"')
      return aEnvironment.getTokenHash().lookUpUnStringify(aSymbol);
    else
      return (String) aEnvironment.getTokenHash().lookUp(aSymbol);
  }

  public static void insert(Environment aEnvironment, int aStackTop,
      boolean aDestructive) throws Exception {
    ConsPointer evaluated = new ConsPointer();
    evaluated.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 1).getCons());
    LispError.checkIsList(aEnvironment, aStackTop, evaluated, 1);

    ConsPointer copied = new ConsPointer();
    if (aDestructive)
      copied.setCons(((ConsPointer) evaluated.car()).getCons());
    else
      Utility.flatCopy(copied, (ConsPointer) evaluated.car());

    ConsPointer index = new ConsPointer();
    index.setCons(BuiltinFunction
        .getArgumentPointer(aEnvironment, aStackTop, 2).getCons());
    LispError
        .checkArgument(aEnvironment, aStackTop, index.getCons() != null, 2);
    LispError.checkArgument(aEnvironment, aStackTop,
        index.car() instanceof String, 2);
    int ind = Integer.parseInt((String) index.car(), 10);
    LispError.checkArgument(aEnvironment, aStackTop, ind > 0, 2);

    ConsTraverser consTraverser = new ConsTraverser(copied);
    while (ind > 0) {
      consTraverser.goNext();
      ind--;
    }

    ConsPointer toInsert = new ConsPointer();
    toInsert.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 3).getCons());
    toInsert.cdr().setCons(consTraverser.getCons());
    consTraverser.getPointer().setCons(toInsert.getCons());
    BuiltinFunction.getTopOfStackPointer(aEnvironment, aStackTop).setCons(
        SublistCons.getInstance(copied.getCons()));
  }

  public static boolean isFalse(Environment aEnvironment,
      ConsPointer aExpression) throws Exception {
    LispError.lispAssert(aExpression.getCons() != null);
    return aExpression.car() instanceof String
        && (String) aExpression.car() == aEnvironment.iFalseString;

    /*
     * Code which returns True for everything except False and {}; return
     * aExpression.car() == aEnvironment.iFalseString || (isSublist(aExpression)
     * && (listLength(aExpression.car()) == 1));
     */
  }

  public static boolean isList(ConsPointer aPtr) throws Exception {
    /**
     * todo:tk: I am currently not sure why non nested lists are not supported
     * in Yacas.
     */
    if (aPtr.getCons() == null)
      return false;

    if (aPtr.type() == Utility.ATOM)
      if (((String) aPtr.car()).equalsIgnoreCase("List"))
        return true;

    if (isSublist(aPtr))
      return true;
    return false;

  }// end method.

  public static boolean isNestedList(ConsPointer clientListPointer)
      throws Exception {

    ConsPointer listPointer = new ConsPointer(clientListPointer.getCons());

    listPointer.goNext(); // Strip List tag.

    while (listPointer.getCons() != null)
      if (listPointer.car() instanceof ConsPointer
          && isList((ConsPointer) listPointer.car()))
        listPointer.goNext();
      else
        return false;
    return true;
  }// end method.

  public static boolean isNumber(String ptr, boolean aAllowFloat) {

    if (ptr.length() == 0)
      return false;

    int pos = 0;
    if (ptr.charAt(pos) == '-' || ptr.charAt(pos) == '+')
      pos++;
    int nrDigits = 0;
    int index = 0;
    if (pos + index == ptr.length())
      return false;
    while (ptr.charAt(pos + index) >= '0' && ptr.charAt(pos + index) <= '9') {
      nrDigits++;
      index++;
      if (pos + index == ptr.length())
        return true;
    }
    if (ptr.charAt(pos + index) == '.') {
      if (!aAllowFloat)
        return false;
      index++;
      if (pos + index == ptr.length())
        return true;
      while (ptr.charAt(pos + index) >= '0' && ptr.charAt(pos + index) <= '9') {
        nrDigits++;
        index++;
        if (pos + index == ptr.length())
          return true;
      }
    }
    if (nrDigits == 0)
      return false;
    if (ptr.charAt(pos + index) == 'e' || ptr.charAt(pos + index) == 'E') {
      if (!aAllowFloat)
        return false;
      if (!BigNumber.numericSupportForMantissa())
        return false;
      index++;
      if (pos + index == ptr.length())
        return true;
      if (ptr.charAt(pos + index) == '-' || ptr.charAt(pos + index) == '+')
        index++;
      while (ptr.charAt(pos + index) >= '0' && ptr.charAt(pos + index) <= '9') {
        index++;
        if (pos + index == ptr.length())
          return true;
      }
    }
    if (ptr.length() != pos + index)
      return false;
    return true;
  }

  public static boolean isString(Object aOriginal) {

    if (!(aOriginal instanceof String))
      return false;

    String stringVersion = (String) aOriginal;

    if (stringVersion != null)
      if (stringVersion.charAt(0) == '\"')
        if (stringVersion.charAt(stringVersion.length() - 1) == '\"')
          return true;
    return false;
  }// end method

  public static boolean isSublist(ConsPointer aPtr) throws Exception {
    /**
     * todo:tk: I am currently not sure why non nested lists are not supported
     * in Yacas.
     */
    if (aPtr.getCons() == null)
      return false;
    if (!(aPtr.car() instanceof ConsPointer))
      return false;
    if (((ConsPointer) aPtr.car()).getCons() == null)
      return false;
    // TODO this StrEqual is far from perfect. We could pass in a Environment
    // object...
    if (!((ConsPointer) aPtr.car()).car().equals("List"))
      return false;
    return true;

  }// end method.

  public static boolean isTrue(Environment aEnvironment, ConsPointer aExpression)
      throws Exception {
    LispError.lispAssert(aExpression.getCons() != null);

    // return aExpression.car() == aEnvironment.iTrueAtom.car();
    return aExpression.car() instanceof String
        && (String) aExpression.car() == aEnvironment.iTrueString;

    /*
     * Code which returns True for everything except False and {}; String
     * expressionString = aExpression.car();
     * 
     * //return expressionString == aEnvironment.iTrueString;
     * 
     * if (expressionString == aEnvironment.iTrueString) { return true; } else
     * if (isSublist(aExpression)) { if (listLength(aExpression.car()) == 1) {
     * //Empty list. return false; } else { //Non-empty list. return true; } }
     * else { //Anything other than False returns true. return expressionString
     * != null && expressionString != aEnvironment.iFalseString; }
     */

  }// end method.

  public static int listLength(ConsPointer aOriginal) throws Exception {
    ConsPointer consTraverser = new ConsPointer(aOriginal.getCons());
    int length = 0;
    while (consTraverser.getCons() != null) {
      consTraverser.goNext();
      length++;
    }
    return length;
  }

  /**
   * Searches for a file on the classpath then in the default directories. If
   * the file is found, it is loaded.
   * 
   * @param aEnvironment
   * @param aFileName
   * @throws java.lang.Exception
   */
  public static void load(Environment aEnvironment, String aFileName)
      throws Exception {
    String oper = unstringify(aFileName);

    String hashedname = (String) aEnvironment.getTokenHash().lookUp(oper);

    InputStatus oldstatus = new InputStatus(aEnvironment.iInputStatus);
    aEnvironment.iInputStatus.setTo(hashedname);

    MathPiperInputStream newInput = null;

    /*
     * java.io.MathPiperInputStream scriptStream =
     * Scripts.getScriptStream(oper); if (scriptStream != null) { newInput = new
     * StandardFileInputStream(scriptStream, aEnvironment.iInputStatus);
     * LispError.check(newInput != null, LispError.FILE_NOT_FOUND);
     * doInternalLoad(aEnvironment, newInput); } else {
     */
    // System.out.println("Loading: " + oper);
    java.net.URL fileURL = java.lang.ClassLoader.getSystemResource(oper);
    if (fileURL != null) // File is on the classpath.
    {
      newInput = new StandardFileInputStream(new InputStreamReader(fileURL
          .openStream()), aEnvironment.iInputStatus);
      LispError.check(newInput != null, LispError.FILE_NOT_FOUND);
      doInternalLoad(aEnvironment, newInput);
    } else
      try {
        // Open file
        newInput = // new StandardFileInputStream(hashedname,
        // aEnvironment.iInputStatus);
        openInputFile(aEnvironment, aEnvironment.iInputDirectories, hashedname,
            aEnvironment.iInputStatus);

        LispError.check(newInput != null, LispError.FILE_NOT_FOUND);
        doInternalLoad(aEnvironment, newInput);
      } catch (Exception e) {
        throw e;
      } finally {
        aEnvironment.iInputStatus.restoreFrom(oldstatus);
      }

    aEnvironment.iInputStatus.restoreFrom(oldstatus);

  }

  public static void loadDefFile(Environment aEnvironment, String aFileName)
      throws Exception {
    LispError.lispAssert(aFileName != null);

    String flatfile = unstringify(aFileName) + ".def";
    DefFile def = aEnvironment.iDefFiles.getFile(aFileName);

    String hashedname = (String) aEnvironment.getTokenHash().lookUp(flatfile);

    InputStatus oldstatus = aEnvironment.iInputStatus;
    aEnvironment.iInputStatus.setTo(hashedname);

    MathPiperInputStream newInput = null;

    /*
     * java.io.MathPiperInputStream scriptStream =
     * Scripts.getScriptStream(flatfile); if (scriptStream != null) { newInput =
     * new StandardFileInputStream(scriptStream, aEnvironment.iInputStatus);
     * LispError.check(newInput != null, LispError.FILE_NOT_FOUND);
     * doLoadDefFile(aEnvironment, newInput, def); } else {
     */
    // System.out.println("Loading: " + flatfile);
    java.net.URL fileURL = java.lang.ClassLoader.getSystemResource(flatfile);
    if (fileURL != null) // File is on the classpath.
    {
      newInput = new StandardFileInputStream(new InputStreamReader(fileURL
          .openStream()), aEnvironment.iInputStatus);
      LispError.check(newInput != null, LispError.FILE_NOT_FOUND);
      doLoadDefFile(aEnvironment, newInput, def);

    } else // File may be in the filesystem.
    {
      newInput = // new StandardFileInputStream(hashedname,
      // aEnvironment.iInputStatus);
      openInputFile(aEnvironment, aEnvironment.iInputDirectories, hashedname,
          aEnvironment.iInputStatus);
      LispError.check(newInput != null, LispError.FILE_NOT_FOUND);
      doLoadDefFile(aEnvironment, newInput, def);
    }

    aEnvironment.iInputStatus.restoreFrom(oldstatus);
  }
  // ////////////////////////////////////////////////
  // /// bits_to_digits and digits_to_bits implementation
  // ////////////////////////////////////////////////

  static double log2TableLookup(int n) throws Exception {
    if (n <= log2_table_size && n >= 2)
      return log2_table[n - 1];
    else
      throw new EvaluationException(
          "log2_table_lookup: error: invalid argument " + n, -1);
  }

  public static void multiFix(Environment aEnvironment, int aStackTop,
      OperatorMap aOps) throws Exception {
    // Get operator
    LispError.checkArgument(aEnvironment, aStackTop, BuiltinFunction
        .getArgumentPointer(aEnvironment, aStackTop, 1).getCons() != null, 1);
    String orig = (String) BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 1).car();
    LispError.checkArgument(aEnvironment, aStackTop, orig != null, 1);

    ConsPointer precedence = new ConsPointer();
    aEnvironment.iLispExpressionEvaluator.evaluate(aEnvironment, precedence,
        BuiltinFunction.getArgumentPointer(aEnvironment, aStackTop, 2));
    LispError.checkArgument(aEnvironment, aStackTop,
        precedence.car() instanceof String, 2);
    int prec = Integer.parseInt((String) precedence.car(), 10);
    LispError.checkArgument(aEnvironment, aStackTop,
        prec <= MathPiperPrinter.KMaxPrecedence, 2);
    aOps.setOperator(prec, Utility.getSymbolName(aEnvironment, orig));
    Utility.putTrueInPointer(aEnvironment, BuiltinFunction
        .getTopOfStackPointer(aEnvironment, aStackTop));
  }

  public static void newRule(Environment aEnvironment, int aStackTop)
      throws Exception {
    // TESTARGS(6);

    int arity;
    int precedence;

    ConsPointer ar = new ConsPointer();
    ConsPointer pr = new ConsPointer();
    ConsPointer predicate = new ConsPointer();
    ConsPointer body = new ConsPointer();
    String orig = null;

    // Get operator
    LispError.checkArgument(aEnvironment, aStackTop, BuiltinFunction
        .getArgumentPointer(aEnvironment, aStackTop, 1).getCons() != null, 1);
    orig = (String) BuiltinFunction.getArgumentPointer(aEnvironment, aStackTop,
        1).car();
    LispError.checkArgument(aEnvironment, aStackTop, orig != null, 1);
    ar.setCons(BuiltinFunction.getArgumentPointer(aEnvironment, aStackTop, 2)
        .getCons());
    pr.setCons(BuiltinFunction.getArgumentPointer(aEnvironment, aStackTop, 3)
        .getCons());
    predicate.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 4).getCons());
    body.setCons(BuiltinFunction.getArgumentPointer(aEnvironment, aStackTop, 5)
        .getCons());

    // The arity
    LispError.checkArgument(aEnvironment, aStackTop, ar.getCons() != null, 2);
    LispError.checkArgument(aEnvironment, aStackTop,
        ar.car() instanceof String, 2);
    arity = Integer.parseInt((String) ar.car(), 10);

    // The precedence
    LispError.checkArgument(aEnvironment, aStackTop, pr.getCons() != null, 3);
    LispError.checkArgument(aEnvironment, aStackTop,
        pr.car() instanceof String, 3);
    precedence = Integer.parseInt((String) pr.car(), 10);

    // Finally define the rule base
    aEnvironment.defineRule(Utility.getSymbolName(aEnvironment, orig), arity,
        precedence, predicate, body);

    // Return true
    Utility.putTrueInPointer(aEnvironment, BuiltinFunction
        .getTopOfStackPointer(aEnvironment, aStackTop));
  }

  public static void newRulePattern(Environment aEnvironment, int aStackTop,
      boolean aMacroMode) throws Exception {
    int arity;
    int precedence;

    ConsPointer arityPointer = new ConsPointer();
    ConsPointer precedencePointer = new ConsPointer();
    ConsPointer predicatePointer = new ConsPointer();
    ConsPointer bodyPointer = new ConsPointer();
    String orig = null;

    // Get operator
    LispError.checkArgument(aEnvironment, aStackTop, BuiltinFunction
        .getArgumentPointer(aEnvironment, aStackTop, 1).getCons() != null, 1);
    orig = (String) BuiltinFunction.getArgumentPointer(aEnvironment, aStackTop,
        1).car();
    LispError.checkArgument(aEnvironment, aStackTop, orig != null, 1);
    arityPointer.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 2).getCons());
    precedencePointer.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 3).getCons());
    predicatePointer.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 4).getCons());
    bodyPointer.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 5).getCons());

    // The arity
    LispError.checkArgument(aEnvironment, aStackTop,
        arityPointer.getCons() != null, 2);
    LispError.checkArgument(aEnvironment, aStackTop,
        arityPointer.car() instanceof String, 2);
    arity = Integer.parseInt((String) arityPointer.car(), 10);

    // The precedence
    LispError.checkArgument(aEnvironment, aStackTop, precedencePointer
        .getCons() != null, 3);
    LispError.checkArgument(aEnvironment, aStackTop,
        precedencePointer.car() instanceof String, 3);
    precedence = Integer.parseInt((String) precedencePointer.car(), 10);

    // Finally define the rule base
    aEnvironment.defineRulePattern(Utility.getSymbolName(aEnvironment, orig),
        arity, precedence, predicatePointer, bodyPointer);

    // Return true
    Utility.putTrueInPointer(aEnvironment, BuiltinFunction
        .getTopOfStackPointer(aEnvironment, aStackTop));
  }

  public static void not(ConsPointer aResult, Environment aEnvironment,
      ConsPointer aExpression) throws Exception {
    if (isTrue(aEnvironment, aExpression))
      putFalseInPointer(aEnvironment, aResult);
    else {
      LispError.check(isFalse(aEnvironment, aExpression),
          LispError.INVALID_ARGUMENT);
      putTrueInPointer(aEnvironment, aResult);
    }
  }

  public static void nth(ConsPointer aResult, ConsPointer aArg, int n)
      throws Exception {
    LispError.check(aArg.getCons() != null, LispError.INVALID_ARGUMENT);
    LispError.check(aArg.car() instanceof ConsPointer,
        LispError.INVALID_ARGUMENT);
    LispError.check(n >= 0, LispError.INVALID_ARGUMENT);
    ConsTraverser consTraverser = new ConsTraverser((ConsPointer) aArg.car());

    while (n > 0) {
      LispError.check(consTraverser.getCons() != null,
          LispError.INVALID_ARGUMENT);
      consTraverser.goNext();
      n--;
    }
    LispError
        .check(consTraverser.getCons() != null, LispError.INVALID_ARGUMENT);
    aResult.setCons(consTraverser.getCons().copy(false));
  }

  public static MathPiperInputStream openInputFile(Environment aEnvironment,
      InputDirectories aInputDirectories, String aFileName,
      InputStatus aInputStatus) throws Exception {
    String othername = aFileName;
    int i = 0;
    MathPiperInputStream f = openInputFile(othername, aInputStatus);
    while (f == null && i < aInputDirectories.size()) {
      othername = (String) aInputDirectories.get(i) + aFileName;
      f = openInputFile(othername, aInputStatus);
      i++;
    }
    return f;
  }

  public static MathPiperInputStream openInputFile(String aFileName,
      InputStatus aInputStatus) throws Exception {// Note:tk:primary method for
    // file opening.
    try {
      if (zipFile != null) {
        java.util.zip.ZipEntry e = zipFile.getEntry(aFileName);
        if (e != null) {
          java.io.InputStream s = zipFile.getInputStream(e);
          return new StandardFileInputStream(new InputStreamReader(s),
              aInputStatus);
        }
      }

      if (aFileName.substring(0, 4).equals("jar:"))
        return new JarFileInputStream(aFileName, aInputStatus);
      else
        return new StandardFileInputStream(aFileName, aInputStatus);
    } catch (Exception e) {
      // MathPiper eats this exception because returning null indicates to
      // higher level code that the file was not found.
    }
    return null;

    // return new StandardFileInputStream(aFileName, aInputStatus);
  }

  public static InfixOperator operatorInfo(Environment aEnvironment,
      int aStackTop, OperatorMap aOperators) throws Exception {
    // Get operator
    LispError.checkArgument(aEnvironment, aStackTop, BuiltinFunction
        .getArgumentPointer(aEnvironment, aStackTop, 1).getCons() != null, 1);

    ConsPointer evaluated = new ConsPointer();
    evaluated.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 1).getCons());

    String orig = (String) evaluated.car();
    LispError.checkArgument(aEnvironment, aStackTop, orig != null, 1);
    //
    InfixOperator op = (InfixOperator) aOperators.lookUp(Utility.getSymbolName(
        aEnvironment, orig));
    return op;
  }

  // lookup table for transforming the number of digits
  // report the table size

  public static Map<String, Comparable> optionsListToJavaMap(ConsPointer argumentsPointer,
      Map<String, Comparable> defaultOptions) throws Exception {

    Map<String, Comparable> userOptions = (Map<String, Comparable>) ((HashMap<String, Comparable>) defaultOptions).clone();

    while (argumentsPointer.getCons() != null) {
      // Obtain -> operator.
      ConsPointer optionPointer = (ConsPointer) argumentsPointer.car();
      LispError.check(optionPointer.type() == Utility.ATOM,
          LispError.INVALID_ARGUMENT);
      String operator = (String) optionPointer.car();
      LispError.check(operator.equals("->"), LispError.INVALID_ARGUMENT);

      // Obtain key.
      optionPointer.goNext();
      LispError.check(optionPointer.type() == Utility.ATOM,
          LispError.INVALID_ARGUMENT);
      String key = (String) optionPointer.car();

      // Obtain value.
      optionPointer.goNext();
      LispError
          .check(optionPointer.type() == Utility.ATOM
              || optionPointer.type() == Utility.NUMBER,
              LispError.INVALID_ARGUMENT);
      if (optionPointer.type() == Utility.ATOM) {
        String value = (String) optionPointer.car();
        value = Utility.stripEndQuotes(value);
        if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false"))
          userOptions.put(key, Boolean.parseBoolean(value));
        else
          userOptions.put(key, value);
      } else // Number
      {
        NumberCons numberCons = (NumberCons) optionPointer.getCons();
        BigNumber bigNumber = (BigNumber) numberCons.getNumber(10);
        Double value = bigNumber.toDouble();
        userOptions.put(key, value);
      }// end if/else.

      argumentsPointer.goNext();

    }// end while

    return userOptions;
  }// end method.

  public static String printExpression(ConsPointer aExpression,
      Environment aEnvironment, int aMaxChars) throws Exception {
    StringBuffer result = new StringBuffer();
    StringOutputStream newOutput = new StringOutputStream(result);
    MathPiperPrinter infixprinter = new MathPiperPrinter(
        aEnvironment.iPrefixOperators, aEnvironment.iInfixOperators,
        aEnvironment.iPostfixOperators, aEnvironment.iBodiedOperators);
    infixprinter.print(aExpression, newOutput, aEnvironment);
    if (aMaxChars > 0 && result.length() > aMaxChars) {
      result.delete(aMaxChars, result.length());
      result.append('.');
      result.append('.');
      result.append('.');
    }
    return result.toString();
  }

  public static void putBooleanInPointer(Environment aEnvironment,
      ConsPointer aResult, boolean aValue) throws Exception {
    if (aValue)
      putTrueInPointer(aEnvironment, aResult);
    else
      putFalseInPointer(aEnvironment, aResult);
  }

  public static void putFalseInPointer(Environment aEnvironment,
      ConsPointer aResult) throws Exception {
    aResult.setCons(aEnvironment.iFalseAtom.copy(false));
  }

  // ************************* The following methods were taken from the
  // Functions class.

  public static void putTrueInPointer(Environment aEnvironment,
      ConsPointer aResult) throws Exception {
    aResult.setCons(aEnvironment.iTrueAtom.copy(false));
  }

  public static void replace(Environment aEnvironment, int aStackTop,
      boolean aDestructive) throws Exception {
    ConsPointer evaluated = new ConsPointer();
    evaluated.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 1).getCons());
    // Ok, so lets not check if it is a list, but it needs to be at least a
    // 'function'
    LispError.checkArgument(aEnvironment, aStackTop,
        evaluated.car() instanceof ConsPointer, 1);

    ConsPointer index = new ConsPointer();
    index.setCons(BuiltinFunction
        .getArgumentPointer(aEnvironment, aStackTop, 2).getCons());
    LispError
        .checkArgument(aEnvironment, aStackTop, index.getCons() != null, 2);
    LispError.checkArgument(aEnvironment, aStackTop,
        index.car() instanceof String, 2);
    int ind = Integer.parseInt((String) index.car(), 10);

    ConsPointer copied = new ConsPointer();
    if (aDestructive)
      copied.setCons(((ConsPointer) evaluated.car()).getCons());
    else
      Utility.flatCopy(copied, (ConsPointer) evaluated.car());
    LispError.checkArgument(aEnvironment, aStackTop, ind > 0, 2);

    ConsTraverser consTraverser = new ConsTraverser(copied);
    while (ind > 0) {
      consTraverser.goNext();
      ind--;
    }

    ConsPointer toInsert = new ConsPointer();
    toInsert.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 3).getCons());
    LispError.checkArgument(aEnvironment, aStackTop,
        consTraverser.getPointer() != null, 2);
    LispError.checkArgument(aEnvironment, aStackTop, consTraverser.getPointer()
        .getCons() != null, 2);
    toInsert.cdr().setCons(consTraverser.getPointer().cdr().getCons());
    consTraverser.getPointer().setCons(toInsert.getCons());
    BuiltinFunction.getTopOfStackPointer(aEnvironment, aStackTop).setCons(
        SublistCons.getInstance(copied.getCons()));
  }

  protected static void returnUnEvaluated(ConsPointer aResult,
      ConsPointer aArguments, Environment aEnvironment) throws Exception {
    ConsPointer full = new ConsPointer();
    full.setCons(aArguments.getCons().copy(false));
    aResult.setCons(SublistCons.getInstance(full.getCons()));

    ConsTraverser consTraverser = new ConsTraverser(aArguments);
    consTraverser.goNext();

    while (consTraverser.getCons() != null) {
      ConsPointer next = new ConsPointer();
      aEnvironment.iLispExpressionEvaluator.evaluate(aEnvironment, next,
          consTraverser.getPointer());
      full.cdr().setCons(next.getCons());
      full.setCons(next.getCons());
      consTraverser.goNext();
    }
    full.cdr().setCons(null);
  }

  public static void reverseList(ConsPointer aResult, ConsPointer aOriginal) {
    // ConsPointer iter = new ConsPointer(aOriginal);
    ConsPointer iter = new ConsPointer();
    iter.setCons(aOriginal.getCons());
    ConsPointer previous = new ConsPointer();
    ConsPointer tail = new ConsPointer();
    tail.setCons(aOriginal.getCons());

    while (iter.getCons() != null) {
      tail.setCons(iter.cdr().getCons());
      iter.cdr().setCons(previous.getCons());
      previous.setCons(iter.getCons());
      iter.setCons(tail.getCons());
    }
    aResult.setCons(previous.getCons());
  }

  /**
   *Implements the MathPiper functions RuleBase and MacroRuleBase . The real
   * work is done by Environment.declareRulebase().
   */
  public static void ruleDatabase(Environment aEnvironment, int aStackTop,
      boolean aListed) throws Exception {
    // TESTARGS(3);

    // Get operator
    ConsPointer argsPointer = new ConsPointer();
    String functionName = null;

    LispError.checkArgument(aEnvironment, aStackTop, BuiltinFunction
        .getArgumentPointer(aEnvironment, aStackTop, 1).getCons() != null, 1);
    functionName = (String) BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 1).car();
    LispError.checkArgument(aEnvironment, aStackTop, functionName != null, 1);
    argsPointer.setCons(BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 2).getCons());

    // Check the arguments.
    LispError.checkIsList(aEnvironment, aStackTop, argsPointer, 2);

    // Finally define the rule database.
    aEnvironment.declareRulebase(Utility.getSymbolName(aEnvironment,
        functionName), ((ConsPointer) argsPointer.car()).cdr(), aListed);

    // Return true
    Utility.putTrueInPointer(aEnvironment, BuiltinFunction
        .getTopOfStackPointer(aEnvironment, aStackTop));
  }

  /**
   * Sets a variable in the current {@link Environment}.
   * 
   * @param aEnvironment
   *          holds the execution environment of the program.
   * @param aStackTop
   * @param aMacroMode
   *          boolean which determines whether the getFirstPointer argument
   *          should be evaluated.
   * @param aGlobalLazyVariable
   * @throws java.lang.Exception
   */
  public static void setVar(Environment aEnvironment, int aStackTop,
      boolean aMacroMode, boolean aGlobalLazyVariable) throws Exception {
    String variableString = null;
    if (aMacroMode) {
      ConsPointer result = new ConsPointer();
      aEnvironment.iLispExpressionEvaluator.evaluate(aEnvironment, result,
          BuiltinFunction.getArgumentPointer(aEnvironment, aStackTop, 1));
      variableString = (String) result.car();
    } else
      variableString = (String) BuiltinFunction.getArgumentPointer(
          aEnvironment, aStackTop, 1).car();
    LispError.checkArgument(aEnvironment, aStackTop, variableString != null, 1);
    LispError.checkArgument(aEnvironment, aStackTop, !Utility.isNumber(
        variableString, true), 1);

    ConsPointer result = new ConsPointer();
    aEnvironment.iLispExpressionEvaluator.evaluate(aEnvironment, result,
        BuiltinFunction.getArgumentPointer(aEnvironment, aStackTop, 2));
    aEnvironment.setGlobalVariable(variableString, result, aGlobalLazyVariable); // Variable
    // setting
    // is
    // deligated
    // to
    // Environment.
    Utility.putTrueInPointer(aEnvironment, BuiltinFunction
        .getTopOfStackPointer(aEnvironment, aStackTop));
  }

  public static void singleFix(int aPrecedence, Environment aEnvironment,
      int aStackTop, OperatorMap aOps) throws Exception {
    // Get operator
    LispError.checkArgument(aEnvironment, aStackTop, BuiltinFunction
        .getArgumentPointer(aEnvironment, aStackTop, 1).getCons() != null, 1);
    String orig = (String) BuiltinFunction.getArgumentPointer(aEnvironment,
        aStackTop, 1).car();
    LispError.checkArgument(aEnvironment, aStackTop, orig != null, 1);
    aOps.setOperator(aPrecedence, Utility.getSymbolName(aEnvironment, orig));
    Utility.putTrueInPointer(aEnvironment, BuiltinFunction
        .getTopOfStackPointer(aEnvironment, aStackTop));
  }

  public static String stripEndQuotes(String aOriginal) throws Exception {
    // If there are not quotes on both ends of the string then return without
    // any changes.
    if (aOriginal.startsWith("\"") && aOriginal.endsWith("\"")) {
      aOriginal = aOriginal.substring(1, aOriginal.length());
      aOriginal = aOriginal.substring(0, aOriginal.length() - 1);
    }// end if.

    return aOriginal;
  }// end method.

  public static void substitute(ConsPointer aTarget, ConsPointer aSource,
      Substitute aBehaviour) throws Exception {
    Cons object = aSource.getCons();
    LispError.lispAssert(object != null);
    if (!aBehaviour.matches(aTarget, aSource)) {
      Object oldList = object.car();

      ConsPointer oldListPointer = null;
      if (oldList instanceof ConsPointer) {
        oldListPointer = (ConsPointer) oldList;
        oldList = null;
      }
      if (oldListPointer != null) {

        ConsPointer newList = new ConsPointer();
        ConsPointer next = newList;
        while (oldListPointer.getCons() != null) {
          substitute(next, oldListPointer, aBehaviour);
          oldListPointer = oldListPointer.cdr();
          next = next.cdr();
        }
        aTarget.setCons(SublistCons.getInstance(newList.getCons()));
      } else
        aTarget.setCons(object.copy(false));
    }// end matches if.
  }

  public static void tail(ConsPointer aResult, ConsPointer aArg)
      throws Exception {
    LispError.check(aArg.getCons() != null, LispError.INVALID_ARGUMENT);
    LispError.check(aArg.car() instanceof ConsPointer,
        LispError.INVALID_ARGUMENT);

    ConsPointer iter = (ConsPointer) aArg.car();

    LispError.check(iter.getCons() != null, LispError.INVALID_ARGUMENT);
    aResult.setCons(SublistCons.getInstance(iter.cdr().getCons()));
  }

  public static String unstringify(String aOriginal) throws Exception {
    LispError.check(aOriginal != null, LispError.INVALID_ARGUMENT);
    LispError.check(aOriginal.charAt(0) == '\"', LispError.INVALID_ARGUMENT);
    int nrc = aOriginal.length() - 1;
    LispError.check(aOriginal.charAt(nrc) == '\"', LispError.INVALID_ARGUMENT);
    return aOriginal.substring(1, nrc);
  }

  public static void use(Environment aEnvironment, String aFileName)
      throws Exception {
    DefFile def = aEnvironment.iDefFiles.getFile(aFileName);
    if (!def.isLoaded()) {
      def.setLoaded();
      load(aEnvironment, aFileName);
    }
  }

  int log2TableRange() {
    return log2_table_size;
  }
  // table look-up of small integer logarithms, for converting the number of
  // digits to binary and back
}// end class.

